/*  SSTextEntryValidation.m
 *  MESLib
 * 
 * Copyright 2003-2009 Starline Software Ltd
 * 
 * This file is part of MESLib.
 * 
 * MESLib is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * MESLib is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with MESLib. If not, see <http://www.gnu.org/licenses/>.
 */
//  Created by Tobias Haughton on Thu Aug 07 2003.
//

#import "SSTextEntryValidation.h"


@implementation SSTextEntryValidation

- (id)initWithInvalidWords:(NSArray *)words errorMessages:(NSArray *)messages forObject:(id)field
{
   if([words count] != [messages count]) {
      //if the 2 arrays have a different count we can't continue
      return nil;
   }

   if (self = [super init]) {
      NSNotificationCenter *nc  = [NSNotificationCenter defaultCenter];

      [nc addObserver:self
             selector:@selector(editingDidBegin:)
                 name:@"NSControlTextDidBeginEditingNotification"
               object:field];
      [nc addObserver:self
             selector:@selector(textDidChange:)
                 name:@"NSControlTextDidChangeNotification"
               object:field];
      
      [self setInvalidWords:words];
      [self setErrorMessages:messages];

      uniqueStrings = [[NSMutableDictionary alloc] init];
   }
   return self;
}

- (id)initWithInvalidWords:(NSArray *)words errorMessages:(NSArray *)messages
{
   if([words count] != [messages count]) {
      //if the 2 arrays have a different count we can't continue
      return nil;
   }

   if (self = [super init]) {
      [self setInvalidWords:words];
      [self setErrorMessages:messages];
      uniqueStrings = [[NSMutableDictionary alloc] init];
   }
   return self;
}

- (void)editingDidBegin:(NSNotification *)note
{
   stringPreEditLength = [[(NSTextView *)[[note userInfo] objectForKey:@"NSFieldEditor"] string] length]; //we need this to make sure text was entered rather than deleted
}

- (void)textDidChange:(NSNotification *)note
{
   int i = 0;
   NSString *string = [[note object] stringValue];
   NSString *subString;
   NSRange editedRange = [(NSTextView *)[[note userInfo] objectForKey:@"NSFieldEditor"] rangeForUserTextChange];

   while(i < [[self invalidWords] count]) {
      NSString *invalidWord = [[self invalidWords] objectAtIndex:i];
      if(editedRange.location >= ([invalidWord length] + 1)) {
         //the edit has happened far enough into the string for it to be this invalid word
         //get the sub string so that we can check
         subString = [string substringWithRange:NSMakeRange((editedRange.location - ([invalidWord length] + 1)), ([invalidWord length]) + 1)];
         if([subString caseInsensitiveCompare:[invalidWord stringByAppendingString:@" "]] == NSOrderedSame) {
            //this string is an invalid word but it may be part of another word
            if((editedRange.location - [invalidWord length] - 1) == 0 ||
               [string characterAtIndex:(editedRange.location - [invalidWord length] - 2)] == ' ') {
               //it is either the first word or a word on its own so it is invalid
               //but has the user just deleted text up to this invalid word?
               if(stringPreEditLength < [[(NSTextView *)[[note userInfo] objectForKey:@"NSFieldEditor"] string] length]) {
                  //text was added so give the warning
                  [self displayAlertWithMessage:[[self errorMessages] objectAtIndex:i]];
               }
               break;
            }
         }
         
      }

      i++;
   }

   stringPreEditLength = [[(NSTextView *)[[note userInfo] objectForKey:@"NSFieldEditor"] string] length]; //we need this to make sure text was entered rather than deleted
}

- (void)scanForInvalidWordsInString:(NSString *)string stringID:(NSString *)identifier
{
   int i = 0;
   int j = 0;
   
   if(![uniqueStrings objectForKey:identifier]) {
      //we have not scanned this string yet so add a reference for it
      [uniqueStrings setObject:[NSMutableDictionary dictionary] forKey:identifier];
   }
   
   while(i < [[self invalidWords] count]) {
      NSRange wordRange = [string rangeOfString:(NSString *)[[self invalidWords] objectAtIndex:i] options:NSCaseInsensitiveSearch];

      while(wordRange.location != NSNotFound) {
         //this invalid word has been found see if we have already warned them about it
         if([[uniqueStrings objectForKey:identifier] objectForKey:[[self invalidWords] objectAtIndex:i]]) {
            //a warning has been issued for this word in this string lets see if it's at the same position
            j = 0;
            NSMutableArray *loopArray = [[uniqueStrings objectForKey:identifier] objectForKey:[[self invalidWords] objectAtIndex:i]];
            BOOL errorShown = NO;
            
            while(j < [loopArray count]) {
               //loop through the array of errors displayed for this word in this string
               if([[loopArray objectAtIndex:j] intValue] == wordRange.location) {
                  errorShown = YES;
                  break;
               }
               j++;
            }

            if(!errorShown) {
               //no error shown for this occurance so display and add to the dictionary
               [[[uniqueStrings objectForKey:identifier] objectForKey:(NSString *)[[self invalidWords] objectAtIndex:i]]
                                                           addObject:[NSNumber numberWithInt:wordRange.location]];
               [self displayAlertWithMessage:[[self errorMessages] objectAtIndex:i]];
               break;
            }
         } else {
            //no error has been displayed for this word in this string
            //add it to the dict and display message
            [[uniqueStrings objectForKey:identifier] setObject:[NSMutableArray array] forKey:[[self invalidWords] objectAtIndex:i]];
            [[[uniqueStrings objectForKey:identifier] objectForKey:(NSString *)[[self invalidWords] objectAtIndex:i]]
                                                           addObject:[NSNumber numberWithInt:wordRange.location]];

            [self displayAlertWithMessage:[[self errorMessages] objectAtIndex:i]];
         }
         
         //see if the word occures further on in the string
         wordRange = [string rangeOfString:(NSString *)[[self invalidWords] objectAtIndex:i]
                                    options:NSCaseInsensitiveSearch
                                   range:NSMakeRange((wordRange.location + wordRange.length), ([string length] - (wordRange.location + wordRange.length)))];
      }
      i++;
   }
}


- (void)displayAlertWithMessage:(NSString *)message
{
   NSRunAlertPanel(NSLocalizedString(@"Alert", nil), message, @"OK", nil, nil);
}

- (NSArray *)invalidWords
{
   return invalidWords;
}

- (NSArray *)errorMessages
{
   return errorMessages;
}

- (void)setInvalidWords:(NSArray *)words
{
   [invalidWords release];
   [words retain];
   invalidWords = words;
}

- (void)setErrorMessages:(NSArray *)messages
{
   [errorMessages release];
   [messages retain];
   errorMessages = messages;
}

- (void)dealloc
{
   [[NSNotificationCenter defaultCenter] removeObserver:self];
   
   [invalidWords release];
   [errorMessages release];
   [uniqueStrings release];
   
   [super dealloc];
}

@end
